
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.ArrayList;
import java.util.List;

public class AstVisitor extends  GoParserBaseVisitor<Object>
{
    public AstTree astTree = new AstTree();
    private Object visit(ParserRuleContext context)
    {
        if(context == null)
            return null;
        return context.accept(this);
    }
    // sourceFile:
    //	packageClause eos (importDecl eos)* (
    //		(functionDecl | methodDecl | declaration) eos
    //	)* EOF;
    @Override
    public Object visitSourceFile(GoParser.SourceFileContext ctx)
    {
        astTree.packageName = (String)visit(ctx.packageClause());
        for(GoParser.ImportDeclContext importDecl : ctx.importDecl())
        {
            Import importNode = (Import)visit(importDecl);
            if(importNode != null)
                astTree.imports.add(importNode);
        }

        List<Decl> decls = new ArrayList<>();
        for(GoParser.FunctionDeclContext functionDeclContext : ctx.functionDecl())
            decls.add((Decl) visit(functionDeclContext));


        for(GoParser.MethodDeclContext methodDeclContext : ctx.methodDecl())
            decls.add((Decl)visit(methodDeclContext));

        astTree.currentScope = astTree.rootScope;
        for(GoParser.DeclarationContext declarationContext : ctx.declaration())
            decls.addAll(utils.typeConversion(visit(declarationContext), Decl.class));

        List<Id> unresolve = new ArrayList<>();
        for(int i=0; i<astTree.unresolve.size(); i++)
        {
            ScopeObject obj = astTree.rootScope.find(astTree.unresolve.get(i).name);
            if(obj == null)
                unresolve.add(astTree.unresolve.get(i));
            else
                astTree.unresolve.get(i).object = obj;
        }
        astTree.unresolve = unresolve;
        astTree.decls = decls;
        return astTree;
    }

    //methodDecl: FUNC receiver IDENTIFIER ( signature block?);
    @Override
    public Object visitMethodDecl(GoParser.MethodDeclContext ctx)
    {
        FuncDecl funcDecl = new FuncDecl();
        astTree.intoScope();
        funcDecl.name = new Id(ctx.IDENTIFIER().getText());
        funcDecl.receiver = utils.typeConversion(ctx.receiver(), Field.class);
        Signature signature = (Signature) visit(ctx.signature());
        funcDecl.type.params = signature.param;
        funcDecl.type.results = signature.results;
        funcDecl.body = (BlockStatement) visit(ctx.block());
        astTree.outScope();
        astTree.declareFunc(funcDecl, ScopeObject.KIND.Func);
        return funcDecl;
    }

    // receiver: parameters;
    @Override
    public Object visitReceiver(GoParser.ReceiverContext ctx)
    {
        return visit(ctx.parameters());
    }

    // packageClause: PACKAGE packageName = IDENTIFIER;
    @Override
    public Object visitPackageClause(GoParser.PackageClauseContext ctx)
    {
        return ctx.packageName.getText();
    }

    //	IMPORT (importSpec | L_PAREN (importSpec eos)* R_PAREN);
    @Override
    public Object visitImportDecl(GoParser.ImportDeclContext ctx)
    {
        Import _import = new Import();
        _import.pathSpec = new ArrayList<>();
        for(GoParser.ImportSpecContext importSpec : ctx.importSpec())
        {
            Import importNode = (Import)visit(importSpec);
            if(importNode.alias != null)
            {
                _import.alias = importNode.alias;
            }
            if(importNode.pathSpec.size() != 0)
            {
                _import.pathSpec.add(importNode.pathSpec.get(0));
            }
        }
        return _import;
    }

    // importSpec: alias = (DOT | IDENTIFIER)? importPath;
    @Override
    public Object visitImportSpec(GoParser.ImportSpecContext ctx)
    {
        Import _import = new Import();
        if(ctx.alias != null)
        {
            _import.alias = new Id(ctx.alias.getText());
        }
        else
            _import.alias = null;
        String name = (String)visit(ctx.importPath());
        _import.pathSpec = new ArrayList<>();
        _import.pathSpec.add(name);
        return _import;
    }

    // importPath: string_;
    @Override
    public Object visitImportPath(GoParser.ImportPathContext ctx)
    {
        return ctx.string_().getText();
    }

    // functionDecl: FUNC IDENTIFIER (signature block?);
    @Override
    public Object visitFunctionDecl(GoParser.FunctionDeclContext ctx)
    {
        FuncDecl func = new FuncDecl();
        func.name = new Id(ctx.IDENTIFIER().getText());
        astTree.intoScope();
        Signature signature = (Signature)visit(ctx.signature());
        func.type.params = signature.param;
        func.type.results = signature.results;
        if(ctx.block() != null)
            func.body = (BlockStatement) visit(ctx.block());
        astTree.outScope();
        astTree.declareFunc(func, ScopeObject.KIND.Func);
        return func;
    }

    // signature:
    //	{noTerminatorAfterParams(1)}? parameters result
    //	| parameters;

    /**
     * 函数的参数和返回值，result是返回值
     * @param ctx
     * @return Signature
     */
    @Override
    public Object visitSignature(GoParser.SignatureContext ctx)
    {
        Signature signature = new Signature();
        signature.param = utils.typeConversion(visit(ctx.parameters()), Field.class);
        if(ctx.result() != null)
            signature.results = utils.typeConversion(visit(ctx.result()), Field.class);
        return signature;
    }

    // result: parameters | type_;
    @Override
    public Object visitResult(GoParser.ResultContext ctx)
    {
        if(ctx.parameters() != null)
            return visit(ctx.parameters());
        else if(ctx.type_() != null)
        {
            List<Field> fields = new ArrayList<>();
            Field field = new Field();
            field.type = (Expr)visit(ctx.type_());
            fields.add(field);
            return fields;
        }
        return null;
    }

    // parameters:
    //	L_PAREN (parameterDecl (COMMA parameterDecl)* COMMA?)? R_PAREN;
    @Override
    public Object visitParameters(GoParser.ParametersContext ctx)
    {
        List<Field> fields = new ArrayList<>();
        for(GoParser.ParameterDeclContext parameterDeclContext : ctx.parameterDecl())
        {
            fields.addAll(utils.typeConversion(visit(parameterDeclContext), Field.class));
        }
        return fields;
    }

    // parameterDecl: identifierList? ELLIPSIS? type_;
    @Override
    public Object visitParameterDecl(GoParser.ParameterDeclContext ctx)
    {
        List<Id> ids = new ArrayList<>();
        List<Field> fields = new ArrayList<>();
        if(ctx.identifierList() != null)
        {
            ids = utils.typeConversion(visit(ctx.identifierList()), Id.class);
        }
        Expr type = (Expr)visit(ctx.type_());
        if(ctx.ELLIPSIS() != null)
        {
            Ellipsis ellipsis = new Ellipsis();
            ellipsis.x = type;
            type = ellipsis;
        }
        for(Id id : ids)
        {
            Field field = new Field();
            field.type = type;
            field.id = id;
            astTree.declareField(field, ScopeObject.KIND.Var, id);
            fields.add(field);
        }

        if(ids.size() == 0)
        {
            Field field = new Field();
            field.id = astTree.requestId();
            field.type = type;
            fields.add(field);
        }
        return fields;
    }

    // identifierList: IDENTIFIER (COMMA IDENTIFIER)*;
    @Override
    public Object visitIdentifierList(GoParser.IdentifierListContext ctx)
    {
        List<Id> ids = new ArrayList<>();
        for(TerminalNode id : ctx.IDENTIFIER())
        {
            ids.add(new Id(id.getText()));
        }
        return ids;
    }

    // type_: typeName | typeLit | L_PAREN type_ R_PAREN;
    @Override
    public Object visitType_(GoParser.Type_Context ctx)
    {
        Expr type = new Expr();
        if(ctx.typeName() != null)
            type = (Expr)visit(ctx.typeName());
        else if(ctx.typeLit() != null)
            type = (Expr)visit(ctx.typeLit());
        else if(ctx.type_() != null)
            type = (Expr)visit(ctx.type_());
        // TODO: 类型不需要解析
        if(type instanceof Id)
        {
            ((Id)type).object = astTree.resolve(type);
        }
        return type;
    }

    // typeName: qualifiedIdent | IDENTIFIER;
    @Override
    public Object visitTypeName(GoParser.TypeNameContext ctx)
    {
        Expr expr = new Expr();
        if(ctx.IDENTIFIER() != null)
        {
            expr = new Id(ctx.IDENTIFIER().getText());
            ((Id)expr).object = astTree.resolve(expr);
        }
        else if(ctx.qualifiedIdent() != null)
        {
            expr = (Expr) visit(ctx.qualifiedIdent());
            ScopeObject obj = astTree.resolve(expr);
            if(obj != null)
                ((SelectorExpr)expr).sel.object = obj;
        }
        return expr;
    }

    // qualifiedIdent: IDENTIFIER DOT IDENTIFIER;
    @Override
    public Object visitQualifiedIdent(GoParser.QualifiedIdentContext ctx)
    {
        SelectorExpr selectorExpr = new SelectorExpr();
        selectorExpr.name = ctx.IDENTIFIER(0).getText();
        selectorExpr.expr = new Id(ctx.IDENTIFIER(0).getText());
        selectorExpr.sel = new Id(ctx.IDENTIFIER(1).getText());

        ScopeObject obj = astTree.resolve(selectorExpr);
        if(obj != null)
        {
            ((Id) selectorExpr.expr).object = obj;
        }
        selectorExpr.name = ctx.IDENTIFIER(1).getText();
        return selectorExpr;
    }

    // typeLit:
    //	arrayType
    //	| structType
    //	| pointerType
    //	| functionType
    //	| interfaceType
    //	| sliceType
    //	| mapType
    //	| channelType;
    @Override
    public Object visitTypeLit(GoParser.TypeLitContext ctx)
    {
        // TODO: 各种type
        if(ctx.arrayType() != null)
            return visit(ctx.arrayType());
        else if(ctx.structType() != null)
            return visit(ctx.structType());
        else if(ctx.pointerType() != null)
            return visit(ctx.pointerType());
        else if(ctx.functionType() != null)
            return visit(ctx.functionType());
        else if(ctx.interfaceType() != null)
            return visit(ctx.interfaceType());
        else if(ctx.sliceType() != null)
            return visit(ctx.sliceType());
        else if(ctx.mapType() != null)
            return visit(ctx.mapType());
        else if(ctx.channelType() != null)
            return visit(ctx.channelType());
        return null;
    }

    // channelType: (CHAN | CHAN RECEIVE | RECEIVE CHAN) elementType;
    @Override
    public Object visitChannelType(GoParser.ChannelTypeContext ctx)
    {
        ChannelType channelType = new ChannelType();
        if(ctx.children.get(0) == ctx.CHAN())
        {
            if(ctx.RECEIVE() != null)
                channelType.dir = ChannelType.ChannelDir.SEND;
            else
                channelType.dir = ChannelType.ChannelDir.TWO;
        }
        else
            channelType.dir = ChannelType.ChannelDir.RECEIVE;
        channelType.type = (Expr)visit(ctx.elementType());
        return channelType;
    }

    // mapType: MAP L_BRACKET type_ R_BRACKET elementType;
    @Override
    public Object visitMapType(GoParser.MapTypeContext ctx)
    {
        MapType mapType = new MapType();
        mapType.keyType = (Expr)visit(ctx.type_());
        mapType.valueType = (Expr)visit(ctx.elementType());
        return mapType;
    }

    // sliceType: L_BRACKET R_BRACKET elementType;
    @Override
    public Object visitSliceType(GoParser.SliceTypeContext ctx)
    {
        Expr expr = (Expr)visit(ctx.elementType());
        SliceExpr sliceExpr = new SliceExpr();
        sliceExpr.x = expr;
        return sliceExpr;
    }

    // elementType: type_;
    @Override
    public Object visitElementType(GoParser.ElementTypeContext ctx)
    {
        return visit(ctx.type_());
    }

    // interfaceType:
    //	INTERFACE L_CURLY ((methodSpec | typeName) eos)* R_CURLY;
    @Override
    public Object visitInterfaceType(GoParser.InterfaceTypeContext ctx)
    {
        List<Field> fields = new ArrayList<>();
        for(GoParser.MethodSpecContext methodSpecContext : ctx.methodSpec())
        {
            fields.add((Field)visit(methodSpecContext));
        }

        for(GoParser.TypeNameContext typeNameContext : ctx.typeName())
        {
            Field field = new Field();
            field.type = (Expr)visit(typeNameContext);
            field.id = null;
            fields.add(field);
        }
        InterfaceType interfaceType = new InterfaceType();
        interfaceType.methods = fields;
        return interfaceType;
    }

    // methodSpec:
    //	{noTerminatorAfterParams(2)}? IDENTIFIER parameters result
    //	| IDENTIFIER parameters;
    @Override
    public Object visitMethodSpec(GoParser.MethodSpecContext ctx)
    {
        Id name = new Id(ctx.IDENTIFIER().getText());
        List<Field> params = utils.typeConversion(visit(ctx.parameters()), Field.class);
        List<Field> results = utils.typeConversion(visit(ctx.result()), Field.class);
        FuncType funcType = new FuncType();
        funcType.params = params;
        funcType.results = results;
        Field field = new Field();
        field.id = name;
        field.name = name.name;
        field.type = funcType;
        return field;
    }

    // functionType: FUNC signature;
    @Override
    public Object visitFunctionType(GoParser.FunctionTypeContext ctx)
    {
        Signature signature = (Signature)visit(ctx.signature());
        FuncType funcType = new FuncType();
        funcType.params = signature.param;
        funcType.results = signature.results;
        return funcType;
    }

    // pointerType: STAR type_;
    @Override
    public Object visitPointerType(GoParser.PointerTypeContext ctx)
    {
        Expr type = (Expr)visit(ctx.type_());
        StarType starType = new StarType();
        starType.type = type;
        return starType;
    }

    // arrayType: L_BRACKET arrayLength R_BRACKET elementType;
    @Override
    public Object visitArrayType(GoParser.ArrayTypeContext ctx)
    {
        Expr len = (Expr)visit(ctx.arrayLength());
        Expr type = (Expr)visit(ctx.elementType());
        ArrayType arrayType = new ArrayType();
        arrayType.len = len;
        arrayType.type = type;
        return arrayType;
    }

    // arrayLength: expression;
    @Override
    public Object visitArrayLength(GoParser.ArrayLengthContext ctx)
    {
        return visit(ctx.expression());
    }

    //    expression:
    //    primaryExpr
    //	| unary_op = (
    //    PLUS
    //		| MINUS
    //		| EXCLAMATION
    //		| CARET
    //		| STAR
    //		| AMPERSAND
    //		| RECEIVE
    //	) expression
    //	| expression mul_op = (
    //        STAR
    //                | DIV
    //                | MOD
    //                | LSHIFT
    //                | RSHIFT
    //                | AMPERSAND
    //                | BIT_CLEAR
    //) expression
    //	| expression add_op = (PLUS | MINUS | OR | CARET) expression
    //	| expression rel_op = (
    //        EQUALS
    //                | NOT_EQUALS
    //                | LESS
    //                | LESS_OR_EQUALS
    //                | GREATER
    //                | GREATER_OR_EQUALS
    //) expression
    //	| expression LOGICAL_AND expression
    //	| expression LOGICAL_OR expression;
    @Override
    public Object visitExpression(GoParser.ExpressionContext ctx)
    {
        if(ctx.primaryExpr() != null)
            return visit(ctx.primaryExpr());
        else if(ctx.unary_op != null)
        {
            UnaryExpr unaryExpr = new UnaryExpr();
            switch(ctx.unary_op.getType())
            {
                case GoParser.PLUS: unaryExpr.op = OP.Add;break;
                case GoParser.MINUS: unaryExpr.op = OP.Sub;break;
                case GoParser.EXCLAMATION: unaryExpr.op = OP.Not;break;
                case GoParser.CARET: unaryExpr.op = OP.Xor;break;
                case GoParser.STAR: unaryExpr.op = OP.Mul;break;
                case GoParser.AMPERSAND: unaryExpr.op = OP.And;break;
                case GoParser.RECEIVE: unaryExpr.op = OP.Recv;break;
            }
            unaryExpr.x = (Expr)visit(ctx.expression(0));
            return unaryExpr;
        }
        else if(ctx.mul_op != null)
        {
            Expression expression = new Expression();
            expression.A = (Expr)visit(ctx.expression(0));
            expression.B = (Expr)visit(ctx.expression(1));
            switch(ctx.mul_op.getType())
            {
                case GoParser.STAR: expression.op = OP.Mul;break;
                case GoParser.DIV: expression.op = OP.Div;break;
                case GoParser.MOD: expression.op = OP.Mod;break;
                case GoParser.LSHIFT: expression.op = OP.Shl;break;
                case GoParser.RSHIFT: expression.op = OP.Shr;break;
                case GoParser.AMPERSAND: expression.op = OP.And;break;
                case GoParser.BIT_CLEAR: expression.op = OP.AndNot;break;
            }
            return expression;
        }
        else if(ctx.add_op != null)
        {
            Expression expression = new Expression();
            expression.A = (Expr)visit(ctx.expression(0));
            expression.B = (Expr)visit(ctx.expression(1));
            switch (ctx.add_op.getType())
            {
                case GoParser.PLUS: expression.op = OP.Add;break;
                case GoParser.MINUS: expression.op = OP.Sub;break;
                case GoParser.OR: expression.op = OP.Or;break;
                case GoParser.CARET: expression.op = OP.And;break;
            }
            return expression;
        }
        else if(ctx.rel_op != null)
        {
            Expression expression = new Expression();
            expression.A = (Expr)visit(ctx.expression(0));
            expression.B = (Expr)visit(ctx.expression(1));
            switch(ctx.rel_op.getType())
            {
                case GoParser.EQUALS: expression.op = OP.Eql;break;
                case GoParser.NOT_EQUALS: expression.op = OP.Neq;break;
                case GoParser.LESS: expression.op = OP.Lss;break;
                case GoParser.LESS_OR_EQUALS: expression.op = OP.Leq;break;
                case GoParser.GREATER: expression.op = OP.Gtr;break;
                case GoParser.GREATER_OR_EQUALS: expression.op = OP.Geq;break;
            }
            return expression;
        }
        else if(ctx.LOGICAL_AND() != null)
        {
            Expression expression = new Expression();
            expression.A = (Expr)visit(ctx.expression(0));
            expression.B = (Expr)visit(ctx.expression(1));
            expression.op = OP.AndAnd;
            return expression;
        }
        else if(ctx.LOGICAL_OR() != null)
        {
            Expression expression = new Expression();
            expression.A = (Expr)visit(ctx.expression(0));
            expression.B = (Expr)visit(ctx.expression(1));
            expression.op = OP.OrOr;
            return expression;
        }
        return null;
    }

    // primaryExpr:
    //	operand
    //	| conversion
    //	| methodExpr
    //	| primaryExpr (
    //		(DOT IDENTIFIER)
    //		| index
    //		| slice_
    //		| typeAssertion
    //		| arguments
    //	);
    @Override
    public Object visitPrimaryExpr(GoParser.PrimaryExprContext ctx)
    {
        if(ctx.operand() != null)
            return visit(ctx.operand());
        else if(ctx.conversion() != null)
            return visit(ctx.conversion());
        else if(ctx.methodExpr() != null)
            return visit(ctx.methodExpr());
        else if(ctx.primaryExpr() != null)
        {
            Expr expr = (Expr)visit(ctx.primaryExpr());
            if(ctx.IDENTIFIER() != null)
            {
                Id name = new Id(ctx.IDENTIFIER().getText());
                name.object = astTree.resolve(name);
                SelectorExpr selectorExpr = new SelectorExpr();
                selectorExpr.expr = expr;
                selectorExpr.sel = name;
                return selectorExpr;
            }
            else if(ctx.index() != null)
            {
                IndexExpr indexExpr = (IndexExpr) visit(ctx.index());
                indexExpr.x = expr;
                return indexExpr;
            }
            else if(ctx.slice_() != null)
            {
                SliceExpr sliceExpr = (SliceExpr) visit(ctx.slice_());
                sliceExpr.x = expr;
                return sliceExpr;
            }
            else if(ctx.typeAssertion() != null)
            {
                TypeAssertExpr typeAssertExpr = (TypeAssertExpr) visit(ctx.typeAssertion());
                typeAssertExpr.x = expr;
                return typeAssertExpr;
            }
            else if(ctx.arguments() != null)
            {
                CallExpr callExpr = (CallExpr) visit(ctx.arguments());
                callExpr.func = expr;
                return callExpr;
            }
        }
        return null;
    }

    // arguments:
    //  L_PAREN (
	//	  (expressionList | nonNamedType (COMMA expressionList)?) ELLIPSIS? COMMA?
    //      )? R_PAREN;
    @Override
    public Object visitArguments(GoParser.ArgumentsContext ctx)
    {
        CallExpr callExpr = new CallExpr();
        callExpr.args = new ArrayList<>();
        if(ctx.expressionList() != null)
            callExpr.args = utils.typeConversion(visit(ctx.expressionList()), Expr.class);
        if(ctx.nonNamedType() != null)
            callExpr.args.add(0, (Expr)visit(ctx.nonNamedType()));
        callExpr.hasDot = ctx.ELLIPSIS() != null;
        return callExpr;
    }

    // typeAssertion: DOT L_PAREN type_ R_PAREN;
    @Override
    public Object visitTypeAssertion(GoParser.TypeAssertionContext ctx)
    {
        TypeAssertExpr typeAssertExpr = new TypeAssertExpr();
        typeAssertExpr.type = (Expr)visit(ctx.type_());
        return typeAssertExpr;
    }

    // slice_:
    //	L_BRACKET (
    //		expression? COLON expression?
    //		| expression? COLON expression COLON expression
    //	) R_BRACKET;
    @Override
    public Object visitSlice_(GoParser.Slice_Context ctx)
    {
        SliceExpr sliceExpr = new SliceExpr();
        int index = 0;
        for(int i=0; i<ctx.expression().size(); i++)
        {
            sliceExpr.index[index++] = (Expr) visit(ctx.expression(i));
        }
        if(index == 3)
            sliceExpr.slice3 = true;
        return sliceExpr;
    }

    // index: L_BRACKET expression R_BRACKET;
    @Override
    public Object visitIndex(GoParser.IndexContext ctx)
    {
        IndexExpr indexExpr = new IndexExpr();
        indexExpr.index = (Expr)visit(ctx.expression());
        return indexExpr;
    }

    // methodExpr: nonNamedType DOT IDENTIFIER;
    @Override
    public Object visitMethodExpr(GoParser.MethodExprContext ctx)
    {
        SelectorExpr selectorExpr = new SelectorExpr();
        selectorExpr.expr = (Expr)visit(ctx.nonNamedType());
        selectorExpr.sel = new Id(ctx.IDENTIFIER().getText());
        return selectorExpr;
    }

    // receiverType: type_;
    @Override
    public Object visitReceiverType(GoParser.ReceiverTypeContext ctx)
    {
        return visit(ctx.type_());
    }

    // conversion: nonNamedType L_PAREN expression COMMA? R_PAREN;
    @Override
    public Object visitConversion(GoParser.ConversionContext ctx)
    {
        CallExpr callExpr = new CallExpr();
        callExpr.func = (Expr)visit(ctx.nonNamedType());
        callExpr.args = new ArrayList<>();
        callExpr.args.add((Expr)visit(ctx.expression()));
        callExpr.hasDot = false;
        return callExpr;
    }

    // nonNamedType: typeLit | L_PAREN nonNamedType R_PAREN;
    @Override
    public Object visitNonNamedType(GoParser.NonNamedTypeContext ctx)
    {
        if(ctx.typeLit() != null)
            return visit(ctx.typeLit());
        else if(ctx.nonNamedType() != null)
            return visit(ctx.nonNamedType());
        return null;
    }

    // operand: literal | operandName | L_PAREN expression R_PAREN;
    @Override
    public Object visitOperand(GoParser.OperandContext ctx)
    {
        if(ctx.literal() != null)
            return visit(ctx.literal());
        else if(ctx.operandName() != null)
            return visit(ctx.operandName());
        else if(ctx.expression() != null)
            return visit(ctx.expression());
        return null;
    }

    // operandName: IDENTIFIER;
    @Override
    public Object visitOperandName(GoParser.OperandNameContext ctx)
    {
        SelectorExpr selectorExpr = new SelectorExpr();
        Id id = new Id(ctx.IDENTIFIER().getText());
        id.object = astTree.resolve(id);
        return id;
    }

    // literal: basicLit | compositeLit | functionLit;

    /**
     * 数值
     * @param ctx
     * @return
     */
    @Override
    public Object visitLiteral(GoParser.LiteralContext ctx)
    {
        if(ctx.basicLit() != null)
            return visit(ctx.basicLit());
        else if(ctx.compositeLit() != null)
            return visit(ctx.compositeLit());
        else if(ctx.functionLit() != null)
            return visit(ctx.functionLit());
        return null;
    }

    // functionLit: FUNC signature block;
    @Override
    public Object visitFunctionLit(GoParser.FunctionLitContext ctx)
    {
        FunctionLit functionLit = new FunctionLit();
        functionLit.signature = (Signature)visit(ctx.signature());
        functionLit.block = (BlockStatement) visit(ctx.block());
        return functionLit;
    }

    // block: L_CURLY statementList? R_CURLY;
    @Override
    public Object visitBlock(GoParser.BlockContext ctx)
    {
        if(ctx.statementList() == null)
            return null;
        BlockStatement blockStatement = new BlockStatement();
        blockStatement.list = utils.typeConversion(visit(ctx.statementList()), Statement.class);
        return blockStatement;
    }

    // statementList: (statement eos)+;
    @Override
    public Object visitStatementList(GoParser.StatementListContext ctx)
    {
        List<Statement> statementList = new ArrayList<>();
        for(GoParser.StatementContext statementContext : ctx.statement())
        {
            statementList.add((Statement) visit(statementContext));
        }
        return statementList;
    }

    // statement:
    //	declaration
    //	| labeledStmt
    //	| simpleStmt
    //	| goStmt
    //	| returnStmt
    //	| breakStmt
    //	| continueStmt
    //	| gotoStmt
    //	| fallthroughStmt
    //	| block
    //	| ifStmt
    //	| switchStmt
    //	| selectStmt
    //	| forStmt
    //	| deferStmt;

    /**
     * 表达式
     * @param ctx
     * @return Statement
     */
    @Override
    public Object visitStatement(GoParser.StatementContext ctx)
    {
        if(ctx.declaration() != null)
        {
            DeclStatement declStatement = new DeclStatement();
            declStatement.decls = utils.typeConversion(visit(ctx.declaration()), Decl.class);
            return declStatement;
        }
        else if(ctx.labeledStmt() != null)
            return visit(ctx.labeledStmt());
        else if(ctx.simpleStmt() != null)
            return visit(ctx.simpleStmt());
        else if(ctx.goStmt() != null)
            return visit(ctx.goStmt());
        else if(ctx.returnStmt() != null)
            return visit(ctx.returnStmt());
        else if(ctx.breakStmt() != null)
            return visit(ctx.breakStmt());
        else if(ctx.continueStmt() != null)
            return visit(ctx.continueStmt());
        else if(ctx.gotoStmt() != null)
            return visit(ctx.gotoStmt());
        else if(ctx.fallthroughStmt() != null)
            return visit(ctx.fallthroughStmt());
        else if(ctx.block() != null)
            return visit(ctx.block());
        else if(ctx.ifStmt() != null)
            return visit(ctx.ifStmt());
        else if(ctx.switchStmt() != null)
            return visit(ctx.switchStmt());
        else if(ctx.switchStmt() != null)
            return visit(ctx.switchStmt());
        else if(ctx.selectStmt() != null)
            return visit(ctx.selectStmt());
        else if(ctx.forStmt() != null)
            return visit(ctx.forStmt());
        else if(ctx.deferStmt() != null)
            return visit(ctx.deferStmt());
        return null;
    }

    // deferStmt: DEFER expression;
    @Override
    public Object visitDeferStmt(GoParser.DeferStmtContext ctx)
    {
        CallStatement callStatement = new CallStatement();
        callStatement.dest = (Expr)visit(ctx.expression());
        callStatement.type = CallStatement.CallType.DEFER;
        return callStatement;
    }

    // forStmt: FOR (expression | forClause | rangeClause)? block;
    @Override
    public Object visitForStmt(GoParser.ForStmtContext ctx)
    {
        ForStatement forStatement = new ForStatement();
        astTree.intoScope();
        if(ctx.expression() != null)
            forStatement.cond = (Expr)visit(ctx.expression());
        else if(ctx.forClause() != null)
            forStatement = (ForStatement) visit(ctx.forClause());
        else if(ctx.rangeClause() != null)
            forStatement = (ForStatement) visit(ctx.rangeClause());
        forStatement.body = (BlockStatement) visit(ctx.block());
        astTree.outScope();
        return forStatement;
    }

    // forClause:
    //	initStmt = simpleStmt? SEMI expression? SEMI postStmt = simpleStmt?;
    @Override
    public Object visitForClause(GoParser.ForClauseContext ctx)
    {
        ForStatement forStatement = new ForStatement();
        if(ctx.initStmt != null)
            forStatement.init = (Statement) visit(ctx.initStmt);
        if(ctx.expression() != null)
            forStatement.cond = (Expr)visit(ctx.expression());
        if(ctx.postStmt != null)
            forStatement.post = (Statement)visit(ctx.postStmt);
        return forStatement;
    }

    // rangeClause: (
    //		expressionList ASSIGN
    //		| identifierList DECLARE_ASSIGN
    //	)? RANGE expression;
    @Override
    public Object visitRangeClause(GoParser.RangeClauseContext ctx)
    {
        RangeClause rangeClause = new RangeClause();
        rangeClause.expr = (Expr)visit(ctx.expression());
        if(ctx.expressionList() != null)
        {
            rangeClause.left = utils.typeConversion(visit(ctx.expressionList()), Expr.class);
            rangeClause.define = false;
        }
        else if(ctx.identifierList() != null)
        {
            rangeClause.left = utils.typeConversion(visit(ctx.identifierList()), Expr.class);
            rangeClause.define = true;
            for(Expr id : rangeClause.left)
            {
                astTree.declareField(rangeClause, ScopeObject.KIND.Var, (Id)id);
            }
        }
        ForStatement forStatement = new ForStatement();
        forStatement.init = rangeClause;
        return forStatement;
    }

    // selectStmt: SELECT L_CURLY commClause* R_CURLY;
    @Override
    public Object visitSelectStmt(GoParser.SelectStmtContext ctx)
    {
        SelectStatement selectStatement = new SelectStatement();
        selectStatement.body = new ArrayList<>();
        for(GoParser.CommClauseContext commClauseContext : ctx.commClause())
        {
            selectStatement.body.add((CommonClause) visit(commClauseContext));
        }
        return selectStatement;
    }

    // commClause: commCase COLON statementList?;
    @Override
    public Object visitCommClause(GoParser.CommClauseContext ctx)
    {
        CommonClause commonClause = new CommonClause();
        commonClause.common = (Statement)visit(ctx.commCase());
        if(ctx.statementList() != null)
            commonClause.body = utils.typeConversion(visit(ctx.statementList()), Statement.class);
        return commonClause;
    }

    // commCase: CASE (sendStmt | recvStmt) | DEFAULT;
    @Override
    public Object visitCommCase(GoParser.CommCaseContext ctx)
    {
        if(ctx.sendStmt() != null)
            return visit(ctx.sendStmt());
        else if(ctx.recvStmt() != null)
            return visit(ctx.recvStmt());
        return null;
    }

    // recvStmt: (expressionList ASSIGN | identifierList DECLARE_ASSIGN)? recvExpr = expression;
    @Override
    public Object visitRecvStmt(GoParser.RecvStmtContext ctx)
    {
        AssignStatement assignStatement = new AssignStatement();
        if(ctx.expressionList() != null)
        {
            assignStatement.left = utils.typeConversion(visit(ctx.expressionList()), Expr.class);
            assignStatement.op = OP.Eq;
        }
        else if(ctx.identifierList() != null)
        {
            assignStatement.left = utils.typeConversion(visit(ctx.identifierList()), Expr.class);
            assignStatement.op = OP.Def;
        }
        assignStatement.right = new ArrayList<>();
        assignStatement.right.add((Expr)visit(ctx.expression()));
        return assignStatement;
    }

    // switchStmt: exprSwitchStmt | typeSwitchStmt;
    @Override
    public Object visitSwitchStmt(GoParser.SwitchStmtContext ctx)
    {
        Statement statement = null;
        astTree.intoScope();
        if(ctx.exprSwitchStmt() != null)
            statement = (Statement)visit(ctx.exprSwitchStmt());
        else if(ctx.typeSwitchStmt() != null)
            statement = (Statement)visit(ctx.typeSwitchStmt());
        astTree.outScope();
        return statement;
    }

    // typeSwitchStmt:
    //	SWITCH (simpleStmt SEMI)? typeSwitchGuard L_CURLY typeCaseClause* R_CURLY;
    @Override
    public Object visitTypeSwitchStmt(GoParser.TypeSwitchStmtContext ctx)
    {
        TypeSwitchStatement typeSwitchStatement = new TypeSwitchStatement();
        if(ctx.simpleStmt() != null)
            typeSwitchStatement.init = (Statement)visit(ctx.simpleStmt());
        typeSwitchStatement.tag = (TypeSwitchGuard) visit(ctx.typeSwitchGuard());
        typeSwitchStatement.body = new ArrayList<>();
        for(GoParser.TypeCaseClauseContext typeCaseClauseContext : ctx.typeCaseClause())
        {
            typeSwitchStatement.body.add((CaseStatement) visit(typeCaseClauseContext));
        }
        return typeSwitchStatement;
    }

    // typeCaseClause: typeSwitchCase COLON statementList?;
    @Override
    public Object visitTypeCaseClause(GoParser.TypeCaseClauseContext ctx)
    {
        CaseStatement caseStatement = new CaseStatement();
        caseStatement.cases = utils.typeConversion(visit(ctx.typeSwitchCase()), Expr.class);
        if(ctx.statementList() != null)
            caseStatement.body = utils.typeConversion(visit(ctx.statementList()), Statement.class);
        return caseStatement;
    }

    // typeSwitchCase: CASE typeList | DEFAULT;
    @Override
    public Object visitTypeSwitchCase(GoParser.TypeSwitchCaseContext ctx)
    {
        if(ctx.DEFAULT() != null)
            return null;
        return visit(ctx.typeList());
    }

    // typeList: (type_ | NIL_LIT) (COMMA (type_ | NIL_LIT))*;
    @Override
    public Object visitTypeList(GoParser.TypeListContext ctx)
    {
        List<Expr> list = new ArrayList<>();
        for(GoParser.Type_Context type_context : ctx.type_())
            list.add((Expr)visit(type_context));
        return list;
    }

    // typeSwitchGuard: (IDENTIFIER DECLARE_ASSIGN)? primaryExpr DOT L_PAREN TYPE R_PAREN;
    @Override
    public Object visitTypeSwitchGuard(GoParser.TypeSwitchGuardContext ctx)
    {
        TypeSwitchGuard typeSwitchStatement = new TypeSwitchGuard();
        if(ctx.IDENTIFIER() != null)
            typeSwitchStatement.name = new Id(ctx.IDENTIFIER().getText());
        typeSwitchStatement.expr = (Expr)visit(ctx.primaryExpr());
        return typeSwitchStatement;
    }

    // exprSwitchCase: CASE expressionList | DEFAULT;
    @Override
    public Object visitExprSwitchCase(GoParser.ExprSwitchCaseContext ctx)
    {
        if(ctx.expressionList() != null)
            return visit(ctx.expressionList());
        return null;
    }

    //    ifStmt:
    //    IF ( expression
    //			| eos expression
    //                    | simpleStmt eos expression
    //    ) block (
    //    ELSE (ifStmt | block)
    //	)?;
    @Override
    public Object visitIfStmt(GoParser.IfStmtContext ctx)
    {
        IFStatement ifStatement = new IFStatement();
        if(ctx.simpleStmt() != null)
            ifStatement.init = (Statement)visit(ctx.simpleStmt());
        ifStatement.condition = (Expr)visit(ctx.expression());
        ifStatement.block = (BlockStatement) visit(ctx.block(0));
        if(ctx.ELSE() != null)
        {
            if(ctx.ifStmt() != null)
                ifStatement.elsePart = (Statement)visit(ctx.ifStmt());
            else if(ctx.block(1) != null)
                ifStatement.elsePart = (Statement)visit(ctx.block(1));
        }
        return ifStatement;
    }

    // fallthroughStmt: FALLTHROUGH;
    @Override
    public Object visitFallthroughStmt(GoParser.FallthroughStmtContext ctx)
    {
        JumpStatement branchStatement = new JumpStatement();
        branchStatement.type = JumpStatement.JumpType.Fallthrough;
        return branchStatement;
    }

    // gotoStmt: GOTO IDENTIFIER;
    @Override
    public Object visitGotoStmt(GoParser.GotoStmtContext ctx)
    {
        JumpStatement branchStatement = new JumpStatement();
        if(ctx.IDENTIFIER() != null)
            branchStatement.label = new Id(ctx.IDENTIFIER().getText());
        branchStatement.type = JumpStatement.JumpType.GOTO;
        return branchStatement;
    }

    // continueStmt: CONTINUE IDENTIFIER?;
    @Override
    public Object visitContinueStmt(GoParser.ContinueStmtContext ctx)
    {
        JumpStatement branchStatement = new JumpStatement();
        if(ctx.IDENTIFIER() != null)
            branchStatement.label = new Id(ctx.IDENTIFIER().getText());
        branchStatement.type = JumpStatement.JumpType.CONTINUE;
        return branchStatement;
    }

    // breakStmt: BREAK IDENTIFIER?;
    @Override
    public Object visitBreakStmt(GoParser.BreakStmtContext ctx)
    {
        JumpStatement branchStatement = new JumpStatement();
        if(ctx.IDENTIFIER() != null)
            branchStatement.label = new Id(ctx.IDENTIFIER().getText());
        branchStatement.type = JumpStatement.JumpType.BREAK;
        return branchStatement;
    }

    // returnStmt: RETURN expressionList?;
    @Override
    public Object visitReturnStmt(GoParser.ReturnStmtContext ctx)
    {
        ReturnStatement returnStatement = new ReturnStatement();
        if(ctx.expressionList() != null)
            returnStatement.results = utils.typeConversion(visit(ctx.expressionList()), Expr.class);
        return returnStatement;
    }

    // goStmt: GO expression;
    @Override
    public Object visitGoStmt(GoParser.GoStmtContext ctx)
    {
        CallStatement callStatement = new CallStatement();
        callStatement.dest = (Expr)visit(ctx.expression());
        callStatement.type = CallStatement.CallType.GO;
        return callStatement;
    }

    // simpleStmt:
    //	sendStmt
    //	| incDecStmt
    //	| assignment
    //	| expressionStmt
    //	| shortVarDecl

    /**
     * 简单表达式
     * assign: 赋值语句
     * @param ctx
     * @return
     */
    @Override
    public Object visitSimpleStmt(GoParser.SimpleStmtContext ctx)
    {
        if(ctx.sendStmt() != null)
            return visit(ctx.sendStmt());
        else if(ctx.incDecStmt() != null)
            return visit(ctx.incDecStmt());
        else if(ctx.assignment() != null)
            return visit(ctx.assignment());
        else if(ctx.expressionStmt() != null)
            return visit(ctx.expressionStmt());
        else if(ctx.shortVarDecl() != null)
            return visit(ctx.shortVarDecl());
        return null;
    }

    // emptyStmt: EOS | SEMI;
    @Override
    public Object visitEmptyStmt(GoParser.EmptyStmtContext ctx)
    {
        return new EmptyStatement();
    }

    // shortVarDecl: identifierList DECLARE_ASSIGN expressionList;
    @Override
    public Object visitShortVarDecl(GoParser.ShortVarDeclContext ctx)
    {
        List<Id> names = utils.typeConversion(visit(ctx.identifierList()), Id.class);
        AssignStatement assignStatement = new AssignStatement();
        assignStatement.right = utils.typeConversion(visit(ctx.expressionList()), Expr.class);
        assignStatement.op = OP.Def;
        assignStatement.left.addAll(names);
        for(Id name : names)
            astTree.declareField(assignStatement, ScopeObject.KIND.Var, name);
        return assignStatement;
    }

    // expressionStmt: expression;
    @Override
    public Object visitExpressionStmt(GoParser.ExpressionStmtContext ctx)
    {
        ExpressionStatement expressionStatement = new ExpressionStatement();
        expressionStatement.expr = (Expr)visit(ctx.expression());
        return expressionStatement;
    }

    // assignment: expressionList assign_op expressionList;
    @Override
    public Object visitAssignment(GoParser.AssignmentContext ctx)
    {
        AssignStatement assignStatement = new AssignStatement();
        assignStatement.left = utils.typeConversion(visit(ctx.expressionList(0)), Expr.class);
        assignStatement.right = utils.typeConversion(visit(ctx.expressionList(1)), Expr.class);
        assignStatement.op = (OP)visit(ctx.assign_op());
        return assignStatement;
    }

    // assign_op: (
    //		PLUS
    //		| MINUS
    //		| OR
    //		| CARET
    //		| STAR
    //		| DIV
    //		| MOD
    //		| LSHIFT
    //		| RSHIFT
    //		| AMPERSAND
    //		| BIT_CLEAR
    //	)? ASSIGN;
    @Override
    public Object visitAssign_op(GoParser.Assign_opContext ctx)
    {
        OP op;
        if(ctx.PLUS() != null)
            op = OP.Add;
        else if(ctx.MINUS() != null)
            op = OP.Sub;
        else if(ctx.OR() != null)
            op = OP.Or;
        else if(ctx.CARET() != null)
            op = OP.Xor;
        else if(ctx.STAR() != null)
            op = OP.Mul;
        else if(ctx.DIV() != null)
            op = OP.Div;
        else if(ctx.MOD() != null)
            op = OP.Mod;
        else if(ctx.LSHIFT() != null)
            op = OP.Shl;
        else if(ctx.RSHIFT() != null)
            op = OP.Shr;
        else if(ctx.AMPERSAND() != null)
            op = OP.And;
        else if(ctx.BIT_CLEAR() != null)
            op = OP.AndNot;
        else op = OP.Eq;
        return op;
    }

    // incDecStmt: expression (PLUS_PLUS | MINUS_MINUS);
    /**
     * 自增自减
     * @param ctx
     * @return IncDecStatement
     */
    @Override
    public Object visitIncDecStmt(GoParser.IncDecStmtContext ctx)
    {
        IncDecStatement incDecStatement = new IncDecStatement();
        incDecStatement.x = (Expr)visit(ctx.expression());
        incDecStatement.op = ctx.PLUS_PLUS() != null ? OP.AddAdd : OP.SubSub;
        return incDecStatement;
    }

    // sendStmt: channel = expression RECEIVE expression;
    @Override
    public Object visitSendStmt(GoParser.SendStmtContext ctx)
    {
        SendStatement sendStatement = new SendStatement();
        sendStatement.channel = (Expr)visit(ctx.channel);
        sendStatement.value = (Expr)visit(ctx.expression(1));
        return sendStatement;
    }

    // labeledStmt: IDENTIFIER COLON statement;
    @Override
    public Object visitLabeledStmt(GoParser.LabeledStmtContext ctx)
    {
        LabelStatement labelStatement = new LabelStatement();
        labelStatement.label = new Id(ctx.IDENTIFIER().getText());
        labelStatement.statement = (Statement) visit(ctx.statement());
        return labelStatement;
    }


    // declaration: constDecl | typeDecl | varDecl;
    @Override
    public Object visitDeclaration(GoParser.DeclarationContext ctx)
    {
        List<Decl> decls = new ArrayList<>();
        if(ctx.constDecl() != null)
        {
            decls = utils.typeConversion(visit(ctx.constDecl()), Decl.class);
            for(Decl decl : decls)
                astTree.declareValue((ValueDecl)decl, ScopeObject.KIND.Const);
        }
        else if(ctx.typeDecl() != null)
        {
            decls = utils.typeConversion(visit(ctx.typeDecl()), Decl.class);
            for(Decl decl : decls)
                astTree.declareType((TypeDecl) decl, ScopeObject.KIND.Type);
        }
        else if(ctx.varDecl() != null)
        {
            decls = utils.typeConversion(visit(ctx.varDecl()), Decl.class);
            for(Decl decl : decls)
                astTree.declareValue((ValueDecl)decl, ScopeObject.KIND.Var);
        }
        return decls;
    }

    // varDecl: VAR (varSpec | L_PAREN (varSpec eos)* R_PAREN);
    @Override
    public Object visitVarDecl(GoParser.VarDeclContext ctx)
    {
        List<Decl> decls = new ArrayList<>();
        for(GoParser.VarSpecContext varSpecContext : ctx.varSpec())
        {
            decls.add((Decl)visit(varSpecContext));
        }
        return decls;
    }

    // varSpec:
    //	identifierList (
    //		type_ (ASSIGN expressionList)?
    //		| ASSIGN expressionList
    //	);

    /**
     * 定义变量 expressionList是变量的赋值
     * @param ctx
     * @return ValueDecl
     */
    @Override
    public Object visitVarSpec(GoParser.VarSpecContext ctx)
    {
        ValueDecl valueDecl = new ValueDecl();
        valueDecl.names = utils.typeConversion(visit(ctx.identifierList()), Id.class);
        valueDecl.type = (Expr)visit(ctx.type_());
        if(ctx.expressionList() != null)
            valueDecl.value = utils.typeConversion(visit(ctx.expressionList()), Expr.class);
        if(valueDecl.names.size() > 1 && valueDecl.type != null)
            valueDecl.isMulti = true;
        return valueDecl;
    }

    // typeDecl: TYPE (typeSpec | L_PAREN (typeSpec eos)* R_PAREN);
    @Override
    public Object visitTypeDecl(GoParser.TypeDeclContext ctx)
    {
        List<Decl> decls = new ArrayList<>();
        for(GoParser.TypeSpecContext typeSpecContext : ctx.typeSpec())
        {
            decls.add((Decl)visit(typeSpecContext));
        }
        return decls;
    }

    // typeSpec: IDENTIFIER ASSIGN? type_;
    @Override
    public Object visitTypeSpec(GoParser.TypeSpecContext ctx)
    {
        TypeDecl typeDecl = new TypeDecl();
        typeDecl.id = new Id(ctx.IDENTIFIER().getText());
        typeDecl.type = (Expr)visit(ctx.type_());
        typeDecl.isAssign = ctx.ASSIGN() != null;
        return typeDecl;
    }

    // constDecl: CONST (constSpec | L_PAREN (constSpec eos)* R_PAREN);
    @Override
    public Object visitConstDecl(GoParser.ConstDeclContext ctx)
    {
        List<Decl> decls = new ArrayList<>();
        for(GoParser.ConstSpecContext constSpecContext : ctx.constSpec())
        {
            decls.add((Decl)visit(constSpecContext));
        }
        return decls;
    }

    // constSpec: identifierList (type_? ASSIGN expressionList)?;
    @Override
    public Object visitConstSpec(GoParser.ConstSpecContext ctx)
    {
        ValueDecl valueDecl = new ValueDecl();
        valueDecl.names = utils.typeConversion(visit(ctx.identifierList()), Id.class);
        if(ctx.type_() != null)
            valueDecl.type = (Expr)visit(ctx.type_());
        if(ctx.expressionList() != null)
            valueDecl.value = utils.typeConversion(visit(ctx.expressionList()), Expr.class);
        if(valueDecl.names.size() > 1 && valueDecl.type != null)
            valueDecl.isMulti = true;
        valueDecl.isConst = true;
        return valueDecl;
    }

    // expressionList: expression (COMMA expression)*;
    @Override
    public Object visitExpressionList(GoParser.ExpressionListContext ctx)
    {
        List<Expr> exprs = new ArrayList<>();
        for(GoParser.ExpressionContext expressionContext : ctx.expression())
        {
            exprs.add((Expr)visit(expressionContext));
        }
        return exprs;
    }

    //    basicLit:
    //    NIL_LIT
    //	| integer
    //	| string_
    //	| FLOAT_LIT;
    @Override
    public Object visitBasicLit(GoParser.BasicLitContext ctx)
    {
        BasicLit baseLit = new BasicLit();
        if(ctx.NIL_LIT() != null)
        {
            Id id = new Id(ctx.NIL_LIT().getText());
            id.object = astTree.resolve(id);
            return id;
        }
        else if(ctx.integer() != null)
        {
            baseLit = (BasicLit)visit(ctx.integer());
        }
        else if(ctx.string_() != null)
        {
            baseLit.kind = LitKind.String;
            baseLit.value = ctx.string_().getText();
        }
        else if(ctx.FLOAT_LIT() != null)
        {
            baseLit.kind = LitKind.Float;
            baseLit.value = ctx.FLOAT_LIT().getText();
        }
        return baseLit;
    }

    // compositeLit: literalType literalValue;
    @Override
    public Object visitCompositeLit(GoParser.CompositeLitContext ctx)
    {
        CompositeLit compositeLit = new CompositeLit();
        compositeLit.type = (Expr)visit(ctx.literalType());
        compositeLit.value = utils.typeConversion(visit(ctx.literalValue()), Expr.class);
        return compositeLit;
    }

    // literalType:
    //	structType
    //	| arrayType
    //	| L_BRACKET ELLIPSIS R_BRACKET elementType
    //	| sliceType
    //	| mapType
    //	| typeName;
    @Override
    public Object visitLiteralType(GoParser.LiteralTypeContext ctx)
    {
        if(ctx.structType() != null)
            return visit(ctx.structType());
        else if(ctx.arrayType() != null)
            return visit(ctx.arrayType());
        else if(ctx.ELLIPSIS() != null)
        {
            Expr type = (Expr)visit(ctx.elementType());
            ArrayType arrayType = new ArrayType();
            arrayType.type = type;
            return arrayType;
        }
        else if(ctx.sliceType() != null)
            return visit(ctx.sliceType());
        else if(ctx.mapType() != null)
            return visit(ctx.mapType());
        else if(ctx.typeName() != null)
            return visit(ctx.typeName());
        return null;
    }


    // structType: STRUCT L_CURLY (fieldDecl eos)* R_CURLY;
    @Override
    public Object visitStructType(GoParser.StructTypeContext ctx)
    {
        StructExpr expr = new StructExpr();
        astTree.intoScope();
        for(GoParser.FieldDeclContext fieldDeclContext : ctx.fieldDecl())
        {
            StructExpr structExpr = (StructExpr)visit(fieldDeclContext);
            expr.fields.addAll(structExpr.fields);
            expr.tags.addAll(structExpr.tags);
        }
        astTree.outScope();
        return expr;
    }

    // fieldDecl: (
    //		{noTerminatorBetween(2)}? identifierList type_
    //		| embeddedField
    //	) tag = string_?;
    @Override
    public Object visitFieldDecl(GoParser.FieldDeclContext ctx)
    {
        List<Field> fields = new ArrayList<>();
        if(ctx.identifierList() != null)
        {
            Expr type = (Expr)visit(ctx.type_());
            List<Id> ids = utils.typeConversion(visit(ctx.identifierList()), Id.class);
            for(Id id : ids)
            {
                Field field = new Field();
                field.id = id;
                field.type = type;
                field.name = id.name;
                fields.add(field);
                astTree.declareField(field, ScopeObject.KIND.Var, field.id);
            }
        }
        else if(ctx.embeddedField() != null)
        {
            fields.add((Field)visit(ctx.embeddedField()));
            Field field = fields.get(0);
            astTree.declareField(field, ScopeObject.KIND.Var, field.id);
        }
        for(Field field : fields)
        {
            if(field.type instanceof Id)
            {
                Id id = (Id) field.type;
                id.object = astTree.resolve((Id) field.type);
            }
        }

        BasicLit basicLit = null;
        if(ctx.string_() != null)
        {
            basicLit = new BasicLit();
            basicLit.kind = LitKind.String;
            basicLit.value = ctx.string_().getText();
        }
        StructExpr struct = new StructExpr();
        struct.fields = fields;
        if(basicLit != null)
            struct.tags.add(basicLit);
        return struct;
    }

    // embeddedField: STAR? typeName;
    @Override
    public Object visitEmbeddedField(GoParser.EmbeddedFieldContext ctx)
    {
        Expr type = (Expr)visit(ctx.typeName());
        if(ctx.STAR() != null)
        {
            StarType starType = new StarType();
            starType.type = type;
            type = starType;
        }
        Field field = new Field();
        field.type = type;
        field.id = astTree.requestId();
        return field;
    }

    // literalValue: L_CURLY (elementList COMMA?)? R_CURLY;
    @Override
    public Object visitLiteralValue(GoParser.LiteralValueContext ctx)
    {
        if(ctx.elementList() == null)
        {
            return null;
        }
        return visit(ctx.elementList());
    }

    // elementList: keyedElement (COMMA keyedElement)*;
    @Override
    public Object visitElementList(GoParser.ElementListContext ctx)
    {
        List<Expr> exprs = new ArrayList<>();
        for(GoParser.KeyedElementContext keyedElementContext : ctx.keyedElement())
        {
            exprs.add((Expr)visit(keyedElementContext));
        }
        return exprs;
    }

    // keyedElement: (key COLON)? element;
    @Override
    public Object visitKeyedElement(GoParser.KeyedElementContext ctx)
    {
        Expr key = null;
        if(ctx.key() != null)
            key = (Expr)visit(ctx.key());
        Expr value = (Expr)visit(ctx.element());
        KeyValueExpr keyValueExpr = new KeyValueExpr();
        keyValueExpr.key = key;
        keyValueExpr.value = value;
        return keyValueExpr;
    }

    // key: expression | literalValue;
    @Override
    public Object visitKey(GoParser.KeyContext ctx)
    {
        if(ctx.expression() != null)
            return visit(ctx.expression());
        else if(ctx.literalValue() != null)
        {
            CompositeExpr compositeExpr = new CompositeExpr();
            compositeExpr.list = utils.typeConversion(visit(ctx.literalValue()), Expr.class);
            compositeExpr.type = null;
            return compositeExpr;
        }
        return null;
    }

    // element: expression | literalValue;
    @Override
    public Object visitElement(GoParser.ElementContext ctx)
    {
        if(ctx.expression() != null)
            return visit(ctx.expression());
        else if(ctx.literalValue() != null)
        {
            CompositeExpr compositeExpr = new CompositeExpr();
            compositeExpr.list = utils.typeConversion(visit(ctx.literalValue()), Expr.class);
            compositeExpr.type = null;
            return compositeExpr;
        }
        return null;
    }

    //  integer:
    //    DECIMAL_LIT
    //	| BINARY_LIT
    //	| OCTAL_LIT
    //	| HEX_LIT
    //	| IMAGINARY_LIT
    //	| RUNE_LIT;
    @Override
    public Object visitInteger(GoParser.IntegerContext ctx)
    {
        BasicLit basicLit = new BasicLit();
        if(ctx.DECIMAL_LIT() != null)
        {
            basicLit.value = ctx.DECIMAL_LIT().getText();
            basicLit.kind = LitKind.DecimalInt;
        }
        else if(ctx.BINARY_LIT() != null)
        {
            basicLit.value = ctx.BINARY_LIT().getText();
            basicLit.kind = LitKind.BinaryInt;
        }
        else if(ctx.OCTAL_LIT() != null)
        {
            basicLit.value = ctx.OCTAL_LIT().getText();
            basicLit.kind = LitKind.OctalInt;
        }
        else if(ctx.HEX_LIT() != null)
        {
            basicLit.value = ctx.HEX_LIT().getText();
            basicLit.kind = LitKind.HexInt;
        }
        else if(ctx.IMAGINARY_LIT() != null)
        {
            basicLit.value = ctx.IMAGINARY_LIT().getText();
            basicLit.kind = LitKind.ImaginaryInt;
        }
        else if(ctx.RUNE_LIT() != null)
        {
            basicLit.value = ctx.RUNE_LIT().getText();
            basicLit.kind = LitKind.RuneInt;
        }
        return basicLit;
    }

    // string_: RAW_STRING_LIT | INTERPRETED_STRING_LIT;
    @Override
    public Object visitString_(GoParser.String_Context ctx)
    {
        if(ctx.RAW_STRING_LIT() != null)
            return ctx.RAW_STRING_LIT().getText();
        else if(ctx.INTERPRETED_STRING_LIT() != null)
            return ctx.INTERPRETED_STRING_LIT().getText();
        return null;
    }
}
